#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
#include <dirent.h>
#include <sys/time.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>

#include <sys/utsname.h>
#include <malloc.h>
#include <net/if.h>
#include <sys/ioctl.h>
#include <linux/sockios.h>
#include <arpa/inet.h>
#include <linux/ethtool.h>
#include <netinet/in.h>


#include "data_type.h"
#include "alloc.h"
#include "memfunc.h"
#include "basefunc.h"
#include "struct_deal.h"
#include "memdb.h"
#include "crypto_func.h"
#include "basetype.h"
#include "message.h"
#include "ex_module.h"
#include "nodeinfo_sender.h"
#include "node_define.h"

int nodeinfo_sender_init(void * sub_proc,void *para)
{
	int ret;
	return 0;
}
int nodeinfo_sender_start(void * sub_proc,void * para)
{
	int ret;
	void *recv_msg;
	int type;
	int subtype;
	//判断收集消息类型决定发送方式
	 
	while(1){
		sleep(2);
		ret=ex_module_recvmsg(sub_proc,&recv_msg);
		if(ret<0){
			continue;
		}
		if(recv_msg=NULL){
			continue;
		}
		type=message_get_type(recv_msg);
		subtype=message_get_subtype(recv_msg);
		if((type==TYPE(NODE_DEFINE))&&(subtype==SUBTYPE(NODE_DEFINE,NODEINFO_REQ))){
			ret=proc_nodeinfo_request(sub_proc,recv_msg);
		}
		else {
			ret=proc_nodeinfo_sender(sub_proc);
      break;
		}
	}
	return 0;
}

/**
 * @brief 获取policy文件的UUID
 * 
 * @return char 
 */
static char*  get_policy_uuid()
{
	//收集白名单摘要值函数 
	DIR *dir=NULL;
	int filesize = 0;
	struct dirent *entry;
	char uuid[64];
	if(!(dir=opendir("/root/centoscloud/cube-whitelist/instance/whitelist_base/policy")))
	{
		printf("opendir failed!");
		return 0;
	}
	else
	{
		entry=readdir(dir);
		strcpy(uuid,entry->d_name);
	}
	closedir(dir);
	return uuid;
}

static int create_socket() {
  int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
  if(sock == -1) {
    return -1;
  }
  return sock;
}

/**
* @description: 获取网卡句柄信息
* @param(sockfd): socket
* @param(ifc): 网卡配置
* @param(buf): 缓存
* @param(buf_size): 缓存大小
**/
static int get_ifconf_r(int sockfd, struct ifconf *ifc, void *buf, int buf_size){
  if(sockfd <= 2 || !ifc || !buf || buf_size <= 0){
    return -1;
  }

  ifc->ifc_len = buf_size;
  ifc->ifc_buf = buf;

  if(ioctl(sockfd, SIOCGIFCONF, ifc) == -1) {
    return -1;
  }
  return 0;
}

/**
 * @brief 获取网卡状态
 * @param sockfd socket信息
 * @param if_name 网卡名称
 * @param if_flag 网卡状态
 * @return int -1失败
 */
static int get_ifstat(int sockfd, const char *if_name, int *if_flag) {
  if (sockfd <= 2 || !if_name || !if_flag) {
    return -1;
  }
  struct ifreq ifr;
  *if_flag = 0;

  strcpy(ifr.ifr_name, if_name);
  if(ioctl(sockfd, SIOCGIFFLAGS, &ifr) == 0) {
    if(ifr.ifr_flags & IFF_UP) *if_flag |= IFSTAT_UP;
    if(ifr.ifr_flags & IFF_BROADCAST) *if_flag |= IFSTAT_BROADCAST;
    if(ifr.ifr_flags & IFF_LOOPBACK) *if_flag |= IFSTAT_LOOPBACK;
    if(ifr.ifr_flags & IFF_POINTOPOINT) *if_flag |= IFSTAT_POINTTOPOINT;
    if(ifr.ifr_flags & IFF_MULTICAST) *if_flag |= IFSTAT_MULTICAST;
  }
  else {
    return -1;
  }
  return 0;
}

/**
 * @brief 得到网口IPv4地址
 * 
 * @param sockfd socket信息
 * @param if_name 网卡名
 * @param addr 待填地址
 * @param addr_len 地址长度
 * @return int -1失败
 */
static int get_ifaddr(int sockfd, const char *if_name, char *addr, int addr_len) {
  if(sockfd <= 2 || !if_name || addr_len <= 0) {
    return -1;
  }
  
  struct ifreq ifr; //网卡返回信息.

  strcpy(ifr.ifr_name, if_name);
  if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) == 0) {
    if (ioctl(sockfd, SIOCGIFADDR, &ifr) == 0) {
      snprintf(addr, addr_len, "%s",inet_ntoa(((struct sockaddr_in*)&(ifr.ifr_addr))->sin_addr));
    }
  }
  else {
    return -1;
  }
  return 0;
}

/**
 * @brief 得到网口MAC地址
 * 
 * @param sockfd socket信息
 * @param if_name 网卡名称
 * @param hwaddr MAC待填地址
 * @param hwaddr_len MAC长度
 * @return int -1失败
 */
static int get_ifhwaddr(int sockfd, const char *if_name, char *hwaddr, int hwaddr_len) {
  if(sockfd <= 2 || !if_name || hwaddr_len <= 0) {
    return -1;
  }
  
  struct ifreq ifr; //网卡返回信息.

  strcpy(ifr.ifr_name, if_name);
  if (ioctl(sockfd, SIOCGIFHWADDR, &ifr) == 0) {
    unsigned char *ptr; //MAC地址字符串指针.
    ptr = (unsigned char *)&ifr.ifr_ifru.ifru_hwaddr.sa_data[0];
    snprintf(hwaddr, hwaddr_len, "%02X:%02X:%02X:%02X:%02X:%02X",
            *ptr, *(ptr+1),*(ptr+2),*(ptr+3),*(ptr+4),*(ptr+5));
  }
  else {
    return -1;
  }
  return 0;
}

/**
 * @brief 获取节点网卡信息
 * 
 * @param info 网卡信息
 * @return int -1失败
 */
static int get_ifinfo(struct ifinfo *info) {
  struct ifconf ifc;  //网卡配置信息
  
  char buf[2048];
  int ret = 0;
  int count = 0;

  int sock = create_socket(); /* 创建socket */

  //printf("socket created...\n");
  
  ret = get_ifconf_r(sock, &ifc, buf, sizeof(buf));
  if(ret != 0) {
    close(sock);
    printf("[ERROR] socket error\n");
    return -1;
  }

  /* 保存网卡结构 */
  struct ifreq* it = ifc.ifc_req;
  /* 得到最后一张网卡的结束地址 */
  const struct ifreq* const end = it + (ifc.ifc_len / sizeof(struct ifreq));

  /* 轮询所有网卡信息 */
  for(; it != end; ++it, count++) {
    memset(info, 0, sizeof(struct ifinfo));
    strcpy(info->if_name, it->ifr_name);
    info->if_idx = if_nametoindex(info->if_name);

    /* 获取网卡信息*/
    if(get_ifstat(sock, info->if_name, &info->if_flag) == -1) printf("[Error] get net interface stat failed\n");
    if(get_ifaddr(sock, info->if_name, info->if_ipv4, sizeof(info->if_ipv4)) == -1) printf("[Error] get interface ipv4 failed\n");
    if(get_ifhwaddr(sock, info->if_name, info->if_mac, sizeof(info->if_mac)) == -1) printf("[Error] get interface MAC failed\n");

		/* 取可用的那张网卡 */
		if((info->if_flag == (IFSTAT_UP | IFSTAT_BROADCAST | IFSTAT_MULTICAST)) || (info->if_flag == (IFSTAT_UP | IFSTAT_BROADCAST | IFSTAT_POINTTOPOINT | IFSTAT_MULTICAST)))
			break;
  }

}

/**
 * @brief 获取宿主机基础信息(除uuid)
 * 
 * @param hostname 宿主机名
 * @param kernel_version 内核版本
 * @param sys_version 系统版本
 * @param machine 设备号
 * @return int -1失败
 */
static int get_baseinfo(char *hostname, char *kernel_version, char *sys_version, char *machine) {
  struct utsname buf;

  if(uname(&buf)) {
    printf("[ERROR] 读取基础信息失败，未能找到utsname.h头文件\n");
    return -1;
  }

  strcpy(hostname, buf.nodename);
  strcpy(kernel_version, buf.release);
  strcpy(sys_version, buf.version);
  strcpy(machine, buf.machine);

  return 0;
}

/**
 * @brief 获取宿主机uuid
 * 
 * @param uuid UUID
 * @return int -1失败
 */
static int get_hostuuid(char *uuid) {
  char *cmd = "dmidecode -s system-uuid";
  FILE *presult_str = NULL;
  char buf[40] = {0};

  presult_str = popen(cmd, "r");
  if(presult_str == NULL) {
    printf("[ERROR] popen 函数执行失败，是否没有安装dmidecode工具？\n");
    return -1;
  }

  /* 获取返回结果 */
  fread(buf, 1, sizeof(buf), presult_str);
  strncpy(uuid, buf, 36);
  uuid[37] = '\0';

  return 0;
}

/**
 * @brief 获取宿主机基本信息.
 * 
 * @param info 宿主机基本信息
 * @return int -1失败
 */
static int get_host_info(struct host_info *info) {

  if(get_baseinfo(info->hostname, 
                  info->kernel_version, 
                  info->sys_version, 
                  info->machine) == -1) {
                    printf("[ERROR] get_baseinfo failed.\n");
                    return -1;
                  }
  if(get_hostuuid(info->host_uuid) == -1) { printf("[ERROR] get_hostuuid failed.\n"); }

  return 0;
}


int proc_nodeinfo_request(void * sub_proc,void * recv_msg)
{
	int ret;
	void * new_msg;

  char  uuid[80];
  struct host_info hostinfo;
  struct ifinfo netinfo;

    /* 获取相关消息 */
	strcpy(uuid,get_policy_uuid());
  ret = get_ifinfo(&netinfo);
  if (ret < 0)
  {
    printf("[ERROR] get ifinfo failed!\n");
    return ret;
  }
  ret = get_host_info(&hostinfo);
  if (ret < 0)
  {
    printf("[ERROR] get hostinfo failed!\n");
    return ret;
  }

	//继承消息路由创建方式 
	new_msg=message_create(TYPE_PAIR(NODE_DEFINE,NODEINFO_REQ),recv_msg);
	if(new_msg==NULL){
		return -EINVAL;
	}
	RECORD(NODE_DEFINE,NODEINFO_REQ) *node_info;
	node_info=Talloc(sizeof(*node_info));
	
	strcpy(uuid,get_policy_uuid());
	//给消息添加数据项!

    Strcpy(node_info->whitelist_uuid,uuid);
	Strcpy(node_info->hostname, hostinfo.hostname);
	Strncpy(node_info->host_uuid, hostinfo.host_uuid,36);
	Strcpy(node_info->kernel_version, hostinfo.kernel_version);
	Strcpy(node_info->sys_version, hostinfo.sys_version);
	Strcpy(node_info->machine, hostinfo.machine);
	Strcpy(node_info->if_ipv4, netinfo.if_ipv4);
	Strcpy(node_info->if_mac, netinfo.if_mac);
	
	
	
	ret = message_add_record(new_msg,node_info);

	if(ret<0)
		return ret;
	ret=ex_module_sendmsg(sub_proc,new_msg);
	return ret;
}


int proc_nodeinfo_sender(void * sub_proc)
{
	int ret;
	int i=0;
	void * recv_msg;

  char  uuid[80];
  struct host_info hostinfo;
  struct ifinfo netinfo;

  /*int type;
	int subtype;*/
	//创建全新消息发送 
	void * new_msg;
	new_msg=message_create(TYPE_PAIR(NODE_DEFINE,NODEINFO_SENDER),NULL);
	RECORD(NODE_DEFINE,NODEINFO_SENDER) *node_info;
	node_info=Talloc(sizeof(*node_info));
	
  /* 获取相关消息 */
	strcpy(uuid,get_policy_uuid());
  ret = get_ifinfo(&netinfo);
  if (ret < 0)
  {
    printf("[ERROR] get ifinfo failed!\n");
    return ret;
  }
  ret = get_host_info(&hostinfo);
  if (ret < 0)
  {
    printf("[ERROR] get hostinfo failed!\n");
    return ret;
  }

  //给消息添加数据项!
  	
  Strcpy(node_info->whitelist_uuid,uuid);
	Strcpy(node_info->hostname, hostinfo.hostname);
	Strncpy(node_info->host_uuid, hostinfo.host_uuid,36);
	Strcpy(node_info->kernel_version, hostinfo.kernel_version);
	Strcpy(node_info->sys_version, hostinfo.sys_version);
	Strcpy(node_info->machine, hostinfo.machine);
	Strcpy(node_info->if_ipv4, netinfo.if_ipv4);
	Strcpy(node_info->if_mac, netinfo.if_mac);
	
	ret = message_add_record(new_msg,node_info);

	if(ret<0)
		return ret;
	ret=ex_module_sendmsg(sub_proc,new_msg);
	return ret;
}

